﻿using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Kyoh.Lib.WinForms
{
    /// <summary>DataGridView に ProgressBar を表示するセル。</summary>
    public class DataGridViewProgressBarCell : DataGridViewTextBoxCell
    {
        //コンストラクタ
        /// <summary><see cref="DataGridViewProgressBarCell"/> クラスの新しいインスタンスを初期化します。</summary>
        public DataGridViewProgressBarCell()
        {
            this.Maximum = 100;
            this.Minimum = 0;
            this.FlatStyle = FlatStyle.Standard;
            this.FlatAppearance = new FlatProgressBarAppearance();
        }

        /// <summary>ProgressBar の最大値を取得・設定します。</summary>
        public int Maximum { get; set; }
        /// <summary>ProgressBar の最小値を取得・設定します。</summary>
        public int Minimum { get; set; }
        /// <summary>進捗率を文字列で表示するかどうかを取得・設定します。</summary>
        public bool DisplayText { get; set; }
        /// <summary><see cref="DataGridViewProgressBarCell"/> の外観を取得または設定します。</summary>
        public FlatStyle FlatStyle { get; set; }

        /// <summary><see cref="DataGridViewProgressBarCell.FlatStyle"/> が FlatStyle.Flat に指定された際に使用するスタイルを取得します。</summary>
        public FlatProgressBarAppearance FlatAppearance { get; private set; }

        /// <summary>セルの値のデータ型を取得します。</summary>
        public override Type ValueType { get { return typeof(int); } }

        /// <summary>新しいレコード行のセルの既定値を取得します。</summary>
        public override object DefaultNewRowValue { get { return 0; } }

        /// <summary>新しいプロパティを追加しているため、Cloneメソッドをオーバーライドします。</summary>
        /// <returns>このオブジェクトのクローン。</returns>
        public override object Clone()
        {
            var cell = (DataGridViewProgressBarCell)base.Clone();
            cell.Maximum = this.Maximum;
            cell.Minimum = this.Minimum;
            cell.DisplayText = this.DisplayText;
            cell.FlatStyle = this.FlatStyle;
            cell.FlatAppearance = (FlatProgressBarAppearance)this.FlatAppearance.Clone();
            return cell;
        }

        /// <summary>セルを描画します。</summary>
        /// <param name="graphics"><see cref="DataGridViewCell" /> の描画に使用する <see cref="Graphics"/>。</param>
        /// <param name="clipBounds">再描画が必要な <see cref="System.Windows.Forms.DataGridView" /> の領域を表す  <see cref="Rectangle" />。</param>
        /// <param name="cellBounds">描画される <see cref="DataGridViewCell"/> の境界が格納された  <see cref="Rectangle" />。</param>
        /// <param name="rowIndex">描画されるセルの行インデックス。</param>
        /// <param name="cellState">セルの状態を指定する <see cref="DataGridViewElementStates"/> 値のビットごとの組み合わせ。</param>
        /// <param name="value">描画される <see cref="DataGridViewCell"/> のデータ。</param>
        /// <param name="formattedValue">描画される <see cref="DataGridViewCell"/> の書式指定済みデータ。</param>
        /// <param name="errorText">セルに関連付けられたエラー メッセージ。</param>
        /// <param name="cellStyle">セルに関する書式とスタイルの情報が格納された <see cref="DataGridViewCellStyle"/>。</param>
        /// <param name="advancedBorderStyle">描画されるセルの境界線スタイルが格納された <see cref="DataGridViewAdvancedBorderStyle"/>。</param>
        /// <param name="paintParts">セルのどの部分を描画する必要があるのかを指定する、<see cref="DataGridViewPaintParts"/> 値のビットごとの組み合わせ。</param>
        protected override void Paint(Graphics graphics,
            Rectangle clipBounds, Rectangle cellBounds,
            int rowIndex, DataGridViewElementStates cellState,
            object value, object formattedValue, string errorText,
            DataGridViewCellStyle cellStyle,
            DataGridViewAdvancedBorderStyle advancedBorderStyle,
            DataGridViewPaintParts paintParts)
        {
            // 値を決定する
            int intValue = 0;
            if (value is int)
                intValue = Math.Min(Math.Max((int)value, this.Minimum), this.Maximum);
            intValue = Math.Min(Math.Max(intValue, this.Minimum), this.Maximum);

            // 割合を計算する
            double rate = (double)(intValue - this.Minimum) / (this.Maximum - this.Minimum);

            // セルの境界線（枠）を描画する
            if ((paintParts & DataGridViewPaintParts.Border) ==
                DataGridViewPaintParts.Border)
            {
                this.PaintBorder(graphics, clipBounds, cellBounds,
                    cellStyle, advancedBorderStyle);
            }

            // 境界線の内側に範囲を取得する
            Rectangle borderRect = this.BorderWidths(advancedBorderStyle);
            Rectangle paintRect = new Rectangle(
                cellBounds.Left + borderRect.Left,
                cellBounds.Top + borderRect.Top,
                cellBounds.Width - borderRect.Width,
                cellBounds.Height - borderRect.Height);

            // 背景色を決定する
            // 選択されている時とされていない時で色を変える
            bool isSelected =
                (cellState & DataGridViewElementStates.Selected) ==
                DataGridViewElementStates.Selected;
            Color bkColor;
            if (isSelected &&
                (paintParts & DataGridViewPaintParts.SelectionBackground) ==
                    DataGridViewPaintParts.SelectionBackground)
            {
                bkColor = cellStyle.SelectionBackColor;
            }
            else
            {
                bkColor = cellStyle.BackColor;
            }

            // 背景を描画する
            if ((paintParts & DataGridViewPaintParts.Background) ==
                DataGridViewPaintParts.Background)
            {
                using (SolidBrush backBrush = new SolidBrush(bkColor))
                {
                    graphics.FillRectangle(backBrush, paintRect);
                }
            }

            // Paddingを差し引く
            paintRect.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top);
            paintRect.Width -= cellStyle.Padding.Horizontal;
            paintRect.Height -= cellStyle.Padding.Vertical;

            // ProgressBarを描画する
            if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground)
            {
                if (this.FlatStyle != FlatStyle.Flat && ProgressBarRenderer.IsSupported)
                {
                    // visualスタイルで描画する

                    // ProgressBarの枠を描画する
                    ProgressBarRenderer.DrawHorizontalBar(graphics, paintRect);
                    // ProgressBarのバーを描画する
                    Rectangle barBounds = new Rectangle(
                        paintRect.Left + 3, paintRect.Top + 3,
                        paintRect.Width - 4, paintRect.Height - 6);
                    barBounds.Width = (int)Math.Round(barBounds.Width * rate);
                    ProgressBarRenderer.DrawHorizontalChunks(graphics, barBounds);
                }
                else
                {
                    // visualスタイルで描画できない時
                    FillRoundRectangle(graphics, new SolidBrush(this.FlatAppearance.BackColor), paintRect, (int)(this.FlatAppearance.Rounded ? paintRect.Height * 0.15f : 0));
                    graphics.DrawRectangle(new Pen(this.FlatAppearance.BorderColor, this.FlatAppearance.BorderWeight), paintRect);
                    Rectangle barBounds = new Rectangle(
                        paintRect.Left + this.FlatAppearance.BorderWeight, paintRect.Top + this.FlatAppearance.BorderWeight,
                        paintRect.Width - this.FlatAppearance.BorderWeight * 2, paintRect.Height - this.FlatAppearance.BorderWeight * 2);
                    barBounds.Width = (int)Math.Round(barBounds.Width * rate);
                    FillRoundRectangle(graphics, new SolidBrush(this.FlatAppearance.ForeColor), barBounds, (int)(this.FlatAppearance.Rounded ? barBounds.Height * 0.15f : 0));
                }
            }

            // フォーカスの枠を表示する
            if (this.DataGridView.CurrentCellAddress.X == this.ColumnIndex &&
                this.DataGridView.CurrentCellAddress.Y == this.RowIndex &&
                (paintParts & DataGridViewPaintParts.Focus) == DataGridViewPaintParts.Focus &&
                this.DataGridView.Focused)
            {
                // フォーカス枠の大きさを適当に決める
                Rectangle focusRect = paintRect;
                focusRect.Inflate(-3, -3);
                //ControlPaint.DrawFocusRectangle(graphics, focusRect);
                // 背景色を指定してフォーカス枠を描画する
                ControlPaint.DrawFocusRectangle(graphics, focusRect, Color.Empty, bkColor);
            }

            // テキストを表示する
            if (this.DisplayText &&
                (paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground)
            {
                // 表示するテキストを決定
                string txt = string.Format("{0}%", Math.Round(rate * 100));
                //string txt = formattedValue.ToString();

                // cellStyleによりTextFormatFlagsを決定する
                TextFormatFlags flags = TextFormatFlags.Default;
                switch (cellStyle.Alignment)
                {
                    case DataGridViewContentAlignment.BottomCenter:
                        flags = TextFormatFlags.Bottom | TextFormatFlags.HorizontalCenter;
                        break;
                    case DataGridViewContentAlignment.BottomLeft:
                        flags = TextFormatFlags.Bottom | TextFormatFlags.Left;
                        break;
                    case DataGridViewContentAlignment.BottomRight:
                        flags = TextFormatFlags.Bottom | TextFormatFlags.Right;
                        break;
                    case DataGridViewContentAlignment.MiddleCenter:
                        flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter;
                        break;
                    case DataGridViewContentAlignment.MiddleLeft:
                        flags = TextFormatFlags.VerticalCenter | TextFormatFlags.Left;
                        break;
                    case DataGridViewContentAlignment.MiddleRight:
                        flags = TextFormatFlags.VerticalCenter | TextFormatFlags.Right;
                        break;
                    case DataGridViewContentAlignment.TopCenter:
                        flags = TextFormatFlags.Top | TextFormatFlags.HorizontalCenter;
                        break;
                    case DataGridViewContentAlignment.TopLeft:
                        flags = TextFormatFlags.Top | TextFormatFlags.Left;
                        break;
                    case DataGridViewContentAlignment.TopRight:
                        flags = TextFormatFlags.Top | TextFormatFlags.Right;
                        break;
                }

                if (cellStyle.WrapMode == DataGridViewTriState.False)
                    flags |= TextFormatFlags.SingleLine;

                //色を決定
                Color fColor = cellStyle.ForeColor;
                
                // テキストを描画する
                paintRect.Inflate(-2, -2);
                TextRenderer.DrawText(graphics, txt, cellStyle.Font,
                    paintRect, fColor, flags);
            }

            // エラーアイコンの表示
            if ((paintParts & DataGridViewPaintParts.ErrorIcon) == DataGridViewPaintParts.ErrorIcon &&
                this.DataGridView.ShowCellErrors &&
                !string.IsNullOrEmpty(errorText))
            {
                // エラーアイコンを表示させる領域を取得
                Rectangle iconBounds = this.GetErrorIconBounds(
                    graphics, cellStyle, rowIndex);
                iconBounds.Offset(cellBounds.X, cellBounds.Y);
                // エラーアイコンを描画
                this.PaintErrorIcon(graphics, iconBounds, cellBounds, errorText);
            }
        }
        private void FillRoundRectangle(Graphics g, Brush brush, Rectangle rectangle, int r)
        {
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.FillPath(brush, GetRoundRect(rectangle, r));
        }

        private static GraphicsPath GetRoundRect(Rectangle rect, int radius)
        {
            GraphicsPath path = new GraphicsPath();
            path.StartFigure();
            // 左上の角丸
            path.AddArc(rect.Left, rect.Top, radius * 2, radius * 2, 180, 90);
            // 上の線
            path.AddLine(rect.Left + radius, rect.Top, rect.Right - radius, rect.Top);
            // 右上の角丸
            path.AddArc(rect.Right - radius * 2, rect.Top, radius * 2, radius * 2, 270, 90);
            // 右の線
            path.AddLine(rect.Right, rect.Top + radius, rect.Right, rect.Bottom - radius);
            // 右下の角丸
            path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);
            // 下の線
            path.AddLine(rect.Right - radius, rect.Bottom, rect.Left + radius, rect.Bottom);
            // 左下の角丸
            path.AddArc(rect.Left, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90);
            // 左の線
            path.AddLine(rect.Left, rect.Bottom - radius, rect.Left, rect.Top + radius);
            path.CloseFigure();
            return path;
        }
    }
}
